package com.spzx.product.service.impl;

import com.spzx.common.core.utils.StringUtils;
import com.spzx.product.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author LOGIN
 * @title TestServiceImpl
 * @create 2025/7/24 17:01
 * @description
 */
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    /**
     * 使用redis实现分布式锁,并且使用uuid作为锁值： set key uuid nx ex 3
     * 避免死锁：
     * 1、通过设置lock锁的过期时间实现redis自动释放锁
     * 2、通过使用finally代码块实现发生异常时自动释放锁
     * 问题一：可能误删其他进程锁；
     * 解决方案：使用uuid作为锁值，释放锁的时候判断uuid，再释放。
     * 问题二：判断uuid的=和释放锁两个操作对于redis来说是两个指令，没有原子性，导致锁的释放可能被其他进程误删：
     * 解决方案：确保使用一个指令完成值获取，值得判断，值的删除，保证redis操作的原子性。
     *
     */
    @Override
    public void testLock() {
        String uuid= UUID.randomUUID().toString();
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,3, TimeUnit.SECONDS);
        if (flag) { //获取锁成功
            try {
                //执行 业务逻辑
                String value = stringRedisTemplate.opsForValue().get("num");
                if (!StringUtils.hasText(value)) {
                    return;
                }
                int valueInt = Integer.parseInt(value);
                stringRedisTemplate.opsForValue().set("num", String.valueOf(++valueInt));
            } catch (NumberFormatException e) {
                throw new RuntimeException(e);
            } finally {
            //4.将分布式锁释放
                //使用redis的del指令
                System.out.println("锁的释放");
                String script = """
                        if redis.call('get', KEYS[1]) == ARGV[1] then
                            local start_time = os.clock()
                            while os.clock() - start_time < 2 do
                            end
                            return redis.call('del', KEYS[1])
                        else
                            return 0
                        end
                        """;
                Boolean lock = stringRedisTemplate.execute(
                        new DefaultRedisScript<>(script, Boolean.class),
                        Arrays.asList("lock"),
                        uuid);
                System.out.println("result = " + lock);
            }
        }else {//锁获取失败
            try {
                Thread.sleep(100);
                testLock();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


    //使用redis的setnx设置分布式锁
    /**
     * 使用redis实现分布式锁,并且使用uuid作为锁值： set key uuid nx ex 3
     * 避免死锁：
     * 1、通过设置lock锁的过期时间实现redis自动释放锁
     * 2、通过使用finally代码块实现发生异常时自动释放锁
     */
    //@Override
    public void testLock2() {
        String uuid= UUID.randomUUID().toString();
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid,2, TimeUnit.SECONDS);
        if (flag) { //获取锁成功
            try {
                //执行 业务逻辑
                String value = stringRedisTemplate.opsForValue().get("num");
                Thread.sleep(1000);
                if (!StringUtils.hasText(value)) {
                    return;
                }
                int valueInt = Integer.parseInt(value);
                stringRedisTemplate.opsForValue().set("num", String.valueOf(++valueInt));
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                //释放锁，使用redis的delete命令
                try {
                    if (uuid.equals(stringRedisTemplate.opsForValue().get("lock"))) {
                        Thread.sleep(2000);
                        stringRedisTemplate.delete("lock");
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }else {//锁获取失败
            try {
                Thread.sleep(100);
                testLock();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


    /*
    * synchoronized 和 Lock 在分布式环境下不生效
    * */
/*    //@Override
    public synchronized void testLock1() {
        String value = stringRedisTemplate.opsForValue().get("num");
        if (!StringUtils.hasText(value)){
            return  ;
        }
        int valueInt = Integer.parseInt(value);
        stringRedisTemplate.opsForValue().set("num", String.valueOf(++valueInt));
    }*/
}
